--[[
    协议编码器
    协议定义: .proto 文件全部放在 proto/[${module}/]*.proto 目录下(通常可以单独作为一个 git 仓库)
    对于不同的模块，通常可以写一个 <mod>.proto 来存储这个模块的协议定义(网关会自动加载这些 .proto 文件)
    协议名称即是编码/解码时使用的协议名称(.proto 中的 message 名称)
    注意，协议 id 不能冲突，否则网关会报错
    每个模块都有 *Proto.lua，返回一个表，其中 key 为协议名(没有 SC_|CS_ 前缀)，值为协议 id
    return {
        LOGIN = 0x0001,
        LOGOUT = 0x0002,
        ...
    }

    协议交互：每个协议在 proto/*.proto 中包含其模板(schema)定义，定义格式为:
    ${PROTO_TYPE}_${PROTO_NAME}
    PROTO_TYPE 为 CS\SC 或者其他前缀，表示传输方向
    PROTO_NAME 为协议名称，通常格式为 ${MOD_NAME}_{NAME}，MOD_NAME 为模块名称，NAME 为协议名称，如：QUERY

    协议处理函数，均放在对应模块 的 *handler 中，每个协议都对应一个处理器，函数原型为:
    function HANDLER.${CS}_${MOD_NAME}_${PROTO_NAME}(player, data)
        -- player 是玩家对象，data 是协议数据
        -- 返回值是一个协议 id 和协议数据，如果返回 nil 或没有返回值则表示不需要返回数据
        return protoId, data
    end
]] --
local skynet = require "skynet"
local PB = require "pb"
local ProtoMgr = require "core.ProtoMgr"

local cs = ProtoMgr:new()
local sc = ProtoMgr:new()

local M = {
    cs = cs,
    sc = sc
}

-- 注册协议
function M.register(protos)
    for name, id in pairs(protos) do
        cs:register(id, "CS_" .. name)
        sc:register(id, "SC_" .. name)
    end
end

function M.loadfile(file)
    local f, err = io.open(file, "rb")
    local schema_data = f:read("*a")
    assert(PB.load(schema_data))
end

function M.encode(protoId, data)
    assert(sc.proto_names[protoId], "unknown proto id: " .. tostring(protoId))
    local obj = {
        id = protoId
    }

    if data then
        obj.data = PB.encode(sc:name(protoId), data)
    end

    return PB.encode("Msg", obj)
end

function M.decode(data)
    local ok, msg = pcall(PB.decode, "Msg", data)
    if not ok then
        LERRF("proto_coder", "error decode msg: %s", data)
        return
    end

    local msgId = msg.id
    if not msg.data then
        return msgId, nil
    end

    local payload_ok, payload = pcall(PB.decode, cs:name(msgId), msg.data)
    if not payload_ok then
        LERRF("proto_coder", "error decode payload, data: %s, message: %s, payload: %s", data, table.dump(msg),
            payload)
        return
    end

    return msgId, payload
end

M.loadfile("modules/Common.pb")

return M
